/*
 * Created on Nov 18, 2003
 */
package org.vectrics.recruiter.job;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;
import org.vectrics.SystemException;
import org.vectrics.common.db.ExpirableUtils;
import org.vectrics.common.db.RecordObject;
import org.vectrics.common.util.DateSync;
import org.vectrics.domain.Domain;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.recruiter.RecruitServiceLocator;
import org.vectrics.recruiter.user.CandidateInfo;
import org.vectrics.recruiter.user.Resume;
import org.vectrics.user.Person;

public class JobCandidate implements RecordObject, Serializable {
	
	public static final long serialVersionUID = 34556216;
	
	private transient Logger log = Logger.getLogger(JobCandidate.class);
	
	public static final int MAX_COVER_LETTER_LENGTH = 5000;
	public static String OBJECT_TYPE = "job.candidate";
	public static String EVENT_CREATED = "job.candidate.created";
	public static String PRIVILEGE_VIEW_CANDIDATES = "job.view.candidates";
	
	private Long id = null;
	private Job job = null;
	private Set statusList = new HashSet();
	private CandidateInfo candidate = null;
	private Person source = null;
	private Person createdBy = null;
	private Resume resume = null;
	private String coverLetter = null;
	private String note = null;
	private boolean candidateSubmit = false;
	private Long strength = new Long(50);
	private Long skillScore = new Long(0);
	private String inactiveReason = null;
	private Date dateAdded = new Date();
	private Date dateAutoAdd = null;
	private Date dateEligible = null;
	private Date dateRejected = null;
	private Date dateDeclined = null;
	private Date dateHired = null;
	private Collection employerRejectReasons = new HashSet();
	private Collection candidateRejectReasons = new HashSet();
	private Set jobCandidateOfferList = new HashSet();
	private JobCandidateCompanyPersonRole jobCandidateCompanyPersonRole = null;
	private boolean voided = false;
   

	public JobCandidate() {
		super();
	}
    
	public Date getDateAutoAdd() {
		return dateAutoAdd;
	}

	public void setDateAutoAdd(Date dateAutoAdd) {
		this.dateAutoAdd = dateAutoAdd;
	}

	public Date getDateDeclined() {
		return dateDeclined;
	}

	public void setDateDeclined(Date dateDeclined) {
		this.dateDeclined = dateDeclined;
	}

	public Date getDateEligible() {
		return dateEligible;
	}

	public void setDateEligible(Date dateEligible) {
		this.dateEligible = dateEligible;
	}

	/**
	 * @return
	 */
	public Date getDateHired() {
		return dateHired;
	}

	/**
	 * @param dateHired
	 */
	public void setDateHired(Date dateHired) {
		this.dateHired = dateHired;
	}

	/**
	 * @return
	 */
	public Date getDateRejected() {
		return dateRejected;
	}

	/**
	 * @param dateRejected
	 */
	public void setDateRejected(Date dateRejected) {
		this.dateRejected = dateRejected;
	}

	/**
	 * @return
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return
	 */
	public String getInactiveReason() {
		return inactiveReason;
	}

	/**
	 * @param inactiveReason
	 */
	public void setInactiveReason(String inactiveReason) {
		this.inactiveReason = inactiveReason;
	}

	
	/**
	 * @return
	 */
	public Job getJob() {
		return this.job;
	}

	/**
	 * @param jobId
	 */
	public void setJob(Job job) {
		this.job = job;
	}

	/**
	 * @return
	 */
	public Long getStrength() {
		return strength;
	}

	/**
	 * @param strength
	 */
	public void setStrength(Long strength) {
		this.strength = strength;
	}

	/**
	 * @return
	 */
	public String getNote() {
		return note;
	}

	/**
	 * @param note
	 */
	public void setNote(String note) {
		this.note = note;
	}

	/**
	 * @return
	 */
	public Date getDateAdded() {
		return dateAdded;
	}

	/**
	 * @param dateAdded
	 */
	public void setDateAdded(Date dateAdded) {
		this.dateAdded = dateAdded;
	}

	/**
	 * @return
	 */
	public Long getSkillScore() {
		return skillScore;
	}

	/**
	 * @param skillScore
	 */
	public void setSkillScore(Long skillScore) {
		this.skillScore = skillScore;
	}

    /**
     * @return Returns the candidate.
     */
    public CandidateInfo getCandidate() {
        return candidate;
    }
    /**
     * @param candidate The candidate to set.
     */
    public void setCandidate(CandidateInfo candidate) {
        this.candidate = candidate;
    }
    /**
     * @return Returns the source.
     */
    public Person getSource() {
        return source;
    }
    /**
     * @param source The source to set.
     */
    public void setSource(Person source) {
        this.source = source;
    }
    
	/**
	 * @return Returns the candidateSubmit.
	 */
	public boolean isCandidateSubmit() {
		return candidateSubmit;
	}
	/**
	 * @param candidateSubmit The candidateSubmit to set.
	 */
	public void setCandidateSubmit(boolean candidateSubmit) {
		this.candidateSubmit = candidateSubmit;
	}
	/**
	 * @return Returns the coverLetter.
	 */
	public String getCoverLetter() {
		return coverLetter;
	}
	/**
	 * @param coverLetter The coverLetter to set.
	 */
	public void setCoverLetter(String coverLetter) {
		this.coverLetter = coverLetter;
	}
	/**
	 * @return Returns the createdBy.
	 */
	public Person getCreatedBy() {
		return createdBy;
	}
	/**
	 * @param createdBy The createdBy to set.
	 */
	public void setCreatedBy(Person createdBy) {
		this.createdBy = createdBy;
	}
	/**
	 * @return Returns the log.
	 */
	public Logger getLog() {
		return log;
	}
	/**
	 * @param log The log to set.
	 */
	public void setLog(Logger log) {
		this.log = log;
	}
	/**
	 * @return Returns the resume.
	 */
	public Resume getResume() {
		return resume;
	}
	/**
	 * @param resume The resume to set.
	 */
	public void setResume(Resume resume) {
		this.resume = resume;
	}
	
	/**
	 * @return Returns the candidateRejectReasons.
	 */
	public Collection getCandidateRejectReasons() {
		return candidateRejectReasons;
	}
	/**
	 * @param candidateRejectReasons The candidateRejectReasons to set.
	 */
	public void setCandidateRejectReasons(Collection candidateRejectReasons) {
		this.candidateRejectReasons = candidateRejectReasons;
	}

	public JobCandidateStatus getCurrentStatus() {
		JobCandidateStatus status = null;
		if (statusList != null) {
			Iterator iter = this.statusList.iterator();
			while (iter.hasNext()) {
				JobCandidateStatus candStatus = (JobCandidateStatus)iter.next();
				if ((candStatus.getVoided() == false) && (candStatus.getDateEnd() == null)) {
					if (status != null)
						throw new SystemException("Unexpected condition, JobCandidate has more than one current status.  JobCandidate.id=" + this.getId());
					status = candStatus;
				}
			}
		} 
		
		return status;
	}
	
	private void expireCurrentStatus() {
		if (this.getId() == null)
			throw new SystemException("Cannot expire a status without job candidate being saved");
		JobCandidateStatus currentStatus = this.getCurrentStatus();
		if (currentStatus != null) {
			if (currentStatus.getId() == null)
				throw new SystemException("Current status was not saved before expiring, jobCandidate.id=" + this.getId());
			
			if (log.isDebugEnabled())
				log.debug("Expiring jobCandidate status: " + currentStatus.getStatusType().getCode()
					+ "  jobCandidateStatus.id = " + currentStatus.getId());
			if (currentStatus.getDateEnd() != null)
				throw new SystemException("Current status is already expired, jobCandidateStatus.id="
						+ currentStatus.getId());
			currentStatus.setDateEnd(DateSync.getCurrentTime());
			ThreadProperties.getSession().update(currentStatus);
		}
	}
	
	
	/**
	 * Sets the status and uses the default reason.
	 * @param statusTypeCode
	 * @param authPerson
	 */
	public void setStatus(String statusTypeCode, Person authPerson) {
		if (this.getId() == null)
			throw new SystemException("JobCandidate should be saved before adding status");
		Domain domain = ThreadProperties.getDomain();
		
		JobCandidateStatus prevCurrentStatus = this.getCurrentStatus();
			
		JobCandidateStatusType statusType = RecruitServiceLocator.getJobProvider()
			.findJobCandidateStatusType(statusTypeCode);
		if (statusType == null)
			throw new SystemException("Could not find status type for code: " + statusTypeCode);
		JobCandidateStatusReasonType defaultReason = null;
		if (prevCurrentStatus == null) {
			defaultReason = RecruitServiceLocator.getJobProvider().findDefaultCandidateStatusReason(
					domain, null, 
						statusType.getCode());
		} else {
			defaultReason = RecruitServiceLocator.getJobProvider().findDefaultCandidateStatusReason(
					domain, prevCurrentStatus.getStatusType().getCode(), 
						statusType.getCode());
		}
		
		if (log.isDebugEnabled())
			log.debug("Transition to " + statusTypeCode + "  Default Reason = " + defaultReason);
		this.setStatus(statusTypeCode, defaultReason, authPerson);
	}
		

	
	public void setStatus(String statusTypeCode, JobCandidateStatusReasonType reasonType, Person authPerson) {
		if (this.getId() == null)
			throw new SystemException("JobCandidate should be saved before adding status");
		
		JobCandidateStatus currentStatus = this.getCurrentStatus();
		if ((currentStatus == null) || (!currentStatus.getStatusType().getCode().equals(statusTypeCode))) {
			JobCandidateStatusType newCandidateStatusType = RecruitServiceLocator.getJobProvider().findJobCandidateStatusType(statusTypeCode);
		
			if (currentStatus != null)
				expireCurrentStatus();

	    	JobCandidateStatus newCandidateStatus = new JobCandidateStatus();
	        newCandidateStatus.setStatusType(newCandidateStatusType);
	        if (currentStatus == null)
	        	newCandidateStatus.setDateStart(DateSync.getCurrentTime());
	        else
	        	newCandidateStatus.setDateStart(currentStatus.getDateEnd());
	        
	        newCandidateStatus.setDateEnd(null);
	        newCandidateStatus.setPerson(authPerson);
	        newCandidateStatus.setJobCandidate(this);

	        newCandidateStatus.setJobCandidateStatusReasonType(reasonType);
	        RecruitServiceLocator.getJobProvider().updateJobCandidateStatus(newCandidateStatus);

	        if (log.isDebugEnabled()) {
				log.debug("Candidate Status change:" +
						"\r\n	- Job:       " + this.getJob().getTitle() + 
						"\r\n	- Candidate: " + this.getCandidate().getPerson().getName() + 
						"\r\n	- From:      " + currentStatus + 
						"\r\n	- To:        " + newCandidateStatus
						);
	        }
	        
	        if (newCandidateStatus.getDateStart() == null)
	        	throw new SystemException("Status date start is null");

	        if (log.isDebugEnabled())
	        	log.debug("Before adding new status to status list, status list size = " + this.statusList.size());
	        this.statusList.add(newCandidateStatus);
	        if (log.isDebugEnabled())
	        	log.debug("Added new status to status list, status list size = " + this.statusList.size());

	        if (log.isDebugEnabled())
	        	log.debug("New candidate status = " + newCandidateStatus);
	        Collection gapProblems = ExpirableUtils.validateNoGapsNoEnd(this.statusList); 
	        if (gapProblems.size() > 0) {
	        	throw new SystemException("Error after adding status, gaps exist:" +
	        			"\r\n  Elements = " + ExpirableUtils.getDebugString(this.statusList) +
	        			"\r\n  Problems: " + gapProblems.toString());
	        } else {
	        	
	        }
	        
	        if (this.getCurrentStatus() == null)
	        	throw new SystemException("Current status not found after adding status.");
	        
		} else {
			
		}
		if (this.getCurrentStatus() == null)
			throw new SystemException("JobCandidate does not have a current status");
	}

	/**
	 * @return Returns the statusList.
	 */
	public Set getStatusList() {
		return statusList;
	}
	/**
	 * @param statusList The statusList to set.
	 */
	public void setStatusList(Set statusList) {
		this.statusList = statusList;
	}
	/**
	 * @return Returns the employerRejectReasons.
	 */
	public Collection getEmployerRejectReasons() {
		return employerRejectReasons;
	}
	/**
	 * @param employerRejectReasons The employerRejectReasons to set.
	 */
	public void setEmployerRejectReasons(Collection employerRejectReasons) {
		this.employerRejectReasons = employerRejectReasons;
	}
	
	/**
	 * @return Returns the jobOfferList.
	 */
	public Set getJobCandidateOfferList() {
		return jobCandidateOfferList;
	}
	/**
	 * @param jobOfferList The jobOfferList to set.
	 */
	public void setJobCandidateOfferList(Set jobOfferList) {
		this.jobCandidateOfferList = jobOfferList;
	}
	
	
	/**
	 * @return Returns the jobCandidateCompanyPersonRole.
	 */
	public JobCandidateCompanyPersonRole getJobCandidateCompanyPersonRole() {
		return jobCandidateCompanyPersonRole;
	}
	/**
	 * @param jobCandidateCompanyPersonRole The jobCandidateCompanyPersonRole to set.
	 */
	public void setJobCandidateCompanyPersonRole(
			JobCandidateCompanyPersonRole jobCandidateCompanyPersonRole) {
		this.jobCandidateCompanyPersonRole = jobCandidateCompanyPersonRole;
	}
	
	public boolean hasCoverLetter() {
		boolean result = false;
		if ((this.getCoverLetter() != null) && (this.getCoverLetter().trim().length() > 0))
			result = true;
		return(result);
	}
	
    /**
     * @return Returns the voided.
     */
    public boolean getVoided() {
        return voided;
    }
    /**
     * @param voided The voided to set.
     */
    public void setVoided(boolean voided) {
        this.voided = voided;
    }
	
    public String toString() {
    	return("JobCandidate{person=" + this.candidate.getPerson() + "|job=" + this.job 
    			+ "|currentStatus=" + this.getCurrentStatus() + 
    			"}");
    }
}
